package 数据结构专栏.面试算法;


import java.io.*;

/**
 * @DESC 在运行期间，保证某个类只创建一个实例，保证一个类仅有一个实例，并提供一个访问它的全局访问点。
 * <p>
 * https://blog.csdn.net/derrantcm/article/details/45330779
 * @Author tzq
 * @Date 2020-03-30 13:00
 **/
public class _面试题2_实现Singleton模式 {

    /**
     * 线程安全，饿汉式，对静态变量初始化一次
     */
    public static class Singleton1 {
        private Singleton1() {
        }

        private final static Singleton1 instance = new Singleton1();

        public static Singleton1 getInstance() {
            return instance;
        }
    }

    /**
     * 防止被通过使用反射的方式进行初始化
     * 反射是可以破坏单例属性的。因为我们通过反射把它的构造函数设成可访问的，然后去生成一个新的对象。
     */
    public static class SingletonTest {
        private final static SingletonTest instance = new SingletonTest();

        public static SingletonTest getInstance() {
            return instance;
        }

        private SingletonTest() {
            System.err.println("SingletonTest Constructor is invoked!");
            if (instance != null) {
                System.err.println("实例已存在，无法初始化！");
                throw new UnsupportedOperationException("实例已存在，无法初始化！");
            }

        }
    }

    /**
     * 对象序列化之后呢？
     */
    public static class SingletonTest2 implements Serializable {
        private SingletonTest2() {
        }
        private final static SingletonTest2 instance = new SingletonTest2();

        public static SingletonTest2 getInstance() {
            return instance;
        }
        private Object readResolve() {
            return instance;
        }
    }


    /**
     * 懒汉 线程不安全
     */
    public static class Singleton2 {
        private Singleton2() {
        }

        private static Singleton2 instance = null;

        public static Singleton2 getInstance() {
            if (instance == null) { // 线程不安全
                instance = new Singleton2();
            }
            return instance;
        }

    }

    /**
     * 单例模式，懒汉式，线程安全，多线程环境下效率不高
     */
    public static class Singleton3 {
        private Singleton3() {
        }

        private static Singleton3 instance = null;

        // 加同步方法锁
        public static synchronized Singleton3 getInstance() {
            if (instance == null) {
                instance = new Singleton3();
            }
            return instance;
        }
    }

    /**
     * 单例模式，懒汉式变种，使用静态代码块，线程安全
     */
    public static class Singleton4 {
        private Singleton4() {
        }

        private static Singleton4 instance = null;

        static {
            instance = new Singleton4();
        }

        public static Singleton4 getInstance() {
            return instance;
        }
    }

    /**
     * 单例模式，使用静态内部类，线程安全【推荐做法】
     * <p>
     * 静态内部类不会在Singleton类加载时就加载，而是在调用getInstance()方法时才进行加载，达到了懒加载的效果。
     * 似乎静态内部类看起来已经是最完美的方法了，其实不是，可能还存在反射攻击或者反序列化攻击
     */
    public static class Singleton5 {
        private Singleton5() {
        }

        private final static class Holder {
            private static Singleton5 instance = new Singleton5();
        }

        public static Singleton5 getInstance() {
            return Holder.instance;
        }

    }

    /**
     * 用枚举方式，线程安全【最佳做法】
     * 直接通过Singleton.INSTANCE.doSomething()的方式调用即可。方便、简洁又安全。
     */
    public static class Singleton6 {
        private Singleton6() {
        }

        public static Singleton6 getInstance() {
            return Singleton6.EnumHolder.INSTANCE.getInstance();
        }

        private enum EnumHolder {
            INSTANCE;
            private Singleton6 instance = null;

            private EnumHolder() {
                instance = new Singleton6();
            }

            private Singleton6 getInstance() {
                return instance;
            }

        }

    }


    /**
     * 使用双重校验锁，线程安全【推荐】
     */
    public static class Singleton7 {
        private Singleton7() {
        }

        /**
         * 引入volatile关键字修饰实例对象，这是为了避免因JVM指令重排序可能导致的空指针异常。
         * 因为当线程执行到第一个if (null == instance)时，代码可能读取到instance不为null，但此时instance引用的对象可能还没有完成初始化。
         */
        private volatile static Singleton7 instance = null;

        public static Singleton7 getInstance() {
            if (null == instance) {
                synchronized (Singleton7.class) { // 类锁
                    if (instance == null) {
                        instance = new Singleton7();
                    }
                }
            }
            return instance;
        }

    }

    /**
     * 模拟多线程验测
     *
     * @param args
     */
    public static void main(String[] args) throws Exception {
//        for (int i = 0; i < 100; i++) {
//            new Thread(() -> {
//                System.out.println(Singleton7.getInstance().hashCode());
//            }).start();
//
//        }


//        Singleton1 instance1 = Singleton1.getInstance();
//        Singleton1 instance2 = Singleton1.getInstance();
//        System.out.println(instance1.hashCode() == instance2.hashCode()); // true
//
//
//        Class clazz = Singleton1.class;
//        Constructor cons = clazz.getDeclaredConstructor(null);
//        cons.setAccessible(true);
//
//        Singleton1 instance3 = (Singleton1) cons.newInstance(null);
//        System.out.println(instance1.hashCode() == instance3.hashCode());  // false

        // 序列化单例对象读取测试
        SingletonTest2 instance1 = SingletonTest2.getInstance();
        ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(new File("tmp.txt")));
        outputStream.writeObject(instance1);
        outputStream.flush();
        outputStream.close();


        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("tmp.txt")));
        SingletonTest2 instance2 = (SingletonTest2) ois.readObject();
        System.out.println(instance1 == instance2); // false

    }

}
